#include "stdafx.h"
#include "CppUnitTest.h"

#include "ImageProcDummyLib.h"
#include "DummyOne.h"
#include "DummyTwo.h"
#include "DummyThree.h"
#include "DummyInvalid.h"

#include "ProcParamInt.h"
#include "ProcParamDouble.h"
#include "ProcParamString.h"

#include "ProcParamDefInt.h"
#include "ProcParamDefDouble.h"
#include "ProcParamDefString.h"

#include "ImageProcStepLibAuto.h"

using namespace Microsoft::VisualStudio::CppUnitTestFramework;

namespace PluginICProcStepsTest
{
	TEST_CLASS(ImageProcStepLibAutoTest)
	{
	private:
		ImageProcStepLibAuto *_Lib;

	public:
		TEST_METHOD_INITIALIZE(InitImageProcStepLibAutoTest)
		{
			_Lib = new ImageProcStepLibAuto();
			_Lib->SetLibNameVersion("ImageProcStepMxImageLib", "v1.0.0.1", 3);
			_Lib->SetLibOwnerDetails("Maximodex Limited", "Copyright 2012, Will Stott", "www.maximodex.com/ProductSupport/ImageCurator/ImageProcLibs/ImageProcSample");
			_Lib->SetLibDescription("dummy - use only for testing - it doesn't do anything");

			_Lib->ClearAllFunctions();
		}

		TEST_METHOD_CLEANUP(DeinitImageProcStepLibAutoTest)
		{
			if (_Lib != '\0')
				delete _Lib;
		}


		TEST_METHOD(CreateImageProcStepLibAutoTest)
		{
			Assert::IsNotNull(_Lib);
			Assert::AreEqual( ImageProcStepLibAuto::NotFound, _Lib->GetFunctionCount());
			Assert::IsFalse(_Lib->Validate());		//cannot validate an empty lib
			Assert::IsFalse(_Lib->IsValid());
		}

		TEST_METHOD(AddFunctionTest)
		{
			Assert::AreEqual(ImageProcStepLibAuto::NotFound, _Lib->GetFunctionCount());

			IImageProcFn *proc = new DummyOne();
			Assert::IsTrue( _Lib->AddFunction(proc));
	
			Assert::AreEqual( ImageProcStepLibAuto::NotFound, _Lib->GetFunctionCount());
			Assert::IsTrue( _Lib->Validate());
			Assert::IsTrue( _Lib->IsValid());
			Assert::AreEqual( 1,  _Lib->GetFunctionCount());

			Assert::AreEqual( DummyOne::FunctionNameDummyOne,  _Lib->GetFunction(0)->GetApi()->GetFunctionName());
			Assert::AreEqual( "this is a dummy function with one i/p param and one o/p param",  _Lib->GetFunction(0)->GetApi()->GetFunctionDescription());
			Assert::AreEqual(  1,  _Lib->GetFunction(0)->GetApi()->GetParamDefCnt(IPluginICProcStepsParamDef::output));
			Assert::AreEqual(  1,  _Lib->GetFunction(DummyOne::FunctionNameDummyOne)->GetApi()->GetParamDefCnt(IPluginICProcStepsParamDef::input));
			Assert::AreEqual(  "brightness",  _Lib->GetFunction(0)->GetApi()->GetParamDef(0,IPluginICProcStepsParamDef::input)->GetName());
			Assert::AreEqual(  IPluginICProcStepsParamAttribOutputNameResult,  _Lib->GetFunction(0)->GetApi()->GetParamDef(0,IPluginICProcStepsParamDef::output)->GetName());

			ProcParamsInputs inputs;
			ProcParamsOutputs outputs;

			Assert::IsTrue( inputs.AddParam(new ProcParamInt(0, "brightness",IPluginICProcStepsParam::input, 3)));
			Assert::IsTrue( inputs.Validate());
			Assert::IsTrue(outputs.AddParam(new ProcParamInt(0, IPluginICProcStepsParamAttribOutputNameResult, IPluginICProcStepsParam::output, 0)));
			Assert::IsTrue(outputs.Validate());

			Assert::IsTrue( _Lib->GetFunction(DummyOne::FunctionNameDummyOne)->GetApi()->IsMatch(DummyOne::FunctionNameDummyOne, &outputs, &inputs));

			Assert::IsTrue( _Lib->ClearAllFunctions());
			Assert::IsFalse( _Lib->IsValid());
		}

		TEST_METHOD(AddThreeFunctionsTest)
		{
			Assert::AreEqual(ImageProcStepLibAuto::NotFound, _Lib->GetFunctionCount());

			DummyOne *proc0 = new DummyOne();
			Assert::IsTrue( _Lib->AddFunction(proc0));

			DummyTwo *proc1 = new DummyTwo();
			Assert::IsTrue( _Lib->AddFunction(proc1));

			DummyThree *proc2 = new DummyThree();
			Assert::IsTrue(_Lib->AddFunction(proc2));

			Assert::IsNull(_Lib->GetFunction(DummyOne::FunctionNameDummyOne));		//can't get functions until _Lib->IsValid() == true
			Assert::IsNull(_Lib->GetFunction(0));
			Assert::IsTrue(_Lib->Validate());
			Assert::IsTrue(_Lib->IsValid());
			Assert::IsNotNull(_Lib->GetFunction(DummyOne::FunctionNameDummyOne));
			Assert::IsNotNull(_Lib->GetFunction(0));

			Assert::AreEqual( 3,  _Lib->GetFunctionCount());

			Assert::AreEqual( DummyOne::FunctionNameDummyOne,  _Lib->GetFunction(DummyOne::FunctionNameDummyOne)->GetApi()->GetFunctionName());
			Assert::AreEqual( DummyTwo::FunctionNameDummyTwo,  _Lib->GetFunction(DummyTwo::FunctionNameDummyTwo)->GetApi()->GetFunctionName());
			Assert::AreEqual( DummyThree::FunctionNameDummyThree,  _Lib->GetFunction(DummyThree::FunctionNameDummyThree)->GetApi()->GetFunctionName());

			Assert::IsTrue(_Lib->ClearAllFunctions());
		}

		TEST_METHOD(AddBadFunctionsTest)
		{
			Assert::AreEqual( ImageProcStepLibAuto::NotFound, _Lib->GetFunctionCount());

			Assert::IsFalse( _Lib->AddFunction('\0'));						//proc is null

			DummyInvalid *proc0 = new DummyInvalid();
			Assert::IsFalse( proc0->IsApiValid());

			Assert::IsFalse( _Lib->AddFunction(proc0));						//proc1 not validated
			Assert::IsTrue(  proc0->UnitTestValidate());
			Assert::IsTrue( _Lib->AddFunction(proc0));						//proc1 validated

			DummyOne *proc1 = new DummyOne();
			Assert::IsTrue( proc1->IsApiValid());
			DummyOne *proc2 = new DummyOne();
			Assert::IsTrue( proc2->IsApiValid());

			Assert::IsTrue(_Lib->AddFunction(proc1));						//add first DummyOne, pass
			Assert::IsFalse(_Lib->AddFunction(proc2));						//add second DummyOne, fails as it has duplicate name

			ImageProcFn *pfn = proc2->GetApi();
			Assert::IsNotNull( pfn);
			Assert::IsTrue( proc2->GetApi()->SetFunctionName("testfn0"));	//invalidates the ImageProcFn object
			Assert::IsNull( proc2->GetApi());								//GetApi() returns '\0' if ImageProcFn object is invalid
			Assert::IsTrue( pfn->Validate());								//make the ImageProcFn object valid
			Assert::IsNotNull( proc2->GetApi());							//GetApi() returns ImageProcFn object now it is valid

			Assert::IsTrue( _Lib->AddFunction(proc2));						//proc name now unique, pass

			Assert::IsTrue(  _Lib->Validate());
			Assert::AreEqual( 3,  _Lib->GetFunctionCount());

			Assert::AreEqual( DummyInvalid::FunctionNameDummyInvalid,  _Lib->GetFunction(DummyInvalid::FunctionNameDummyInvalid)->GetApi()->GetFunctionName());
			Assert::AreEqual( DummyOne::FunctionNameDummyOne,  _Lib->GetFunction(DummyOne::FunctionNameDummyOne)->GetApi()->GetFunctionName());
			Assert::AreEqual( "testfn0",  _Lib->GetFunction("testfn0")->GetApi()->GetFunctionName());

			Assert::IsNull(_Lib->GetFunction("xxx"));
			Assert::IsNull(_Lib->GetFunction(-1) );

			Assert::IsNotNull(_Lib->GetFunction(0));
			Assert::IsNotNull(_Lib->GetFunction(1));
			Assert::IsNotNull(_Lib->GetFunction(2));

			Assert::IsNull( _Lib->GetFunction(3));

			Assert::IsTrue( _Lib->ClearAllFunctions());
		}

		TEST_METHOD(CreateFunctionsTest)
		{
			ImageProcFn *proc0 = new ImageProcFn();
			Assert::IsFalse(proc0->IsValid());
			ImageProcFn *procCopy = new ImageProcFn(proc0);		//invalid function copies to invalid function
			Assert::IsFalse(procCopy->IsValid());			
			delete procCopy;

			procCopy = new ImageProcFn('\0');						//invalid function copies to invalid function
			Assert::IsFalse( procCopy->IsValid());	
			Assert::AreEqual( ImageProcFn::Invalid,  procCopy->GetParamDefCnt(IPluginICProcStepsParamDef::input));	
			Assert::AreEqual( ImageProcFn::Invalid,  procCopy->GetParamDefCnt(IPluginICProcStepsParamDef::output));
			Assert::IsNull(  procCopy->GetParamDef(0, IPluginICProcStepsParamDef::input));
			Assert::IsNull(  procCopy->GetParamDef(0, IPluginICProcStepsParamDef::output));
			delete procCopy;

			proc0->SetFunctionName("testfn1");									
			proc0->SetFunctionDescription("a dummy function");
			proc0->AddParamDef(new ProcParamDefDouble(0, "zeroI", "zero", IPluginICProcStepsParamDef::input, 0.0, 10.0));
			proc0->AddParamDef(new ProcParamDefInt(1, "oneI", "one", IPluginICProcStepsParamDef::input, 0, 10));
			proc0->AddParamDef(new ProcParamDefString(2, "twoI", "two", IPluginICProcStepsParamDef::input, 0, 10));
			proc0->AddParamDef(new ProcParamDefDouble(0, IPluginICProcStepsParamAttribOutputNameResult, "zero", IPluginICProcStepsParamDef::output, 0.0, 10.0));
			proc0->AddParamDef(new ProcParamDefInt(1, "oneO", "one", IPluginICProcStepsParamDef::output, 0, 10));
			proc0->AddParamDef(new ProcParamDefString(2, "twoO", "two", IPluginICProcStepsParamDef::output, 0, 10));

			procCopy = new ImageProcFn(proc0);			//invalid function copies to invalid function
			Assert::IsFalse( procCopy->IsValid());	
			Assert::AreEqual(ImageProcFn::Invalid,  procCopy->GetParamDefCnt(IPluginICProcStepsParamDef::input));	
			Assert::AreEqual(ImageProcFn::Invalid,  procCopy->GetParamDefCnt(IPluginICProcStepsParamDef::output));
			Assert::IsFalse(  procCopy->GetParamDef(0, IPluginICProcStepsParamDef::input) != '\0');
			Assert::IsFalse(  procCopy->GetParamDef(0, IPluginICProcStepsParamDef::output) != '\0');
			delete procCopy;

			Assert::IsTrue( proc0->Validate());			//validate function
			Assert::IsTrue( proc0->IsValid());			

			procCopy = new ImageProcFn(proc0);			//valid function now copies to valid function
			delete proc0;
			proc0 = '\0';
			Assert::IsTrue( procCopy->IsValid());		//check this really is a copy
			Assert::AreEqual( 3,  procCopy->GetParamDefCnt(IPluginICProcStepsParamDef::input));	
			Assert::AreEqual( 3,  procCopy->GetParamDefCnt(IPluginICProcStepsParamDef::output));
			Assert::IsTrue(  IPluginICProcStepsParamDef::doubleP == procCopy->GetParamDef(0,IPluginICProcStepsParamDef::output)->GetTypeP());
			Assert::IsTrue(  IPluginICProcStepsParamDef::intP ==  procCopy->GetParamDef(1,IPluginICProcStepsParamDef::output)->GetTypeP());
			Assert::IsTrue(  IPluginICProcStepsParamDef::stringP ==  procCopy->GetParamDef(2,IPluginICProcStepsParamDef::output)->GetTypeP());
			Assert::IsTrue(  IPluginICProcStepsParamDef::doubleP == procCopy->GetParamDef(0,IPluginICProcStepsParamDef::input)->GetTypeP());
			Assert::IsTrue(  IPluginICProcStepsParamDef::intP == procCopy->GetParamDef(1,IPluginICProcStepsParamDef::input)->GetTypeP());
			Assert::IsTrue(  IPluginICProcStepsParamDef::stringP == procCopy->GetParamDef(2,IPluginICProcStepsParamDef::input)->GetTypeP());
														//check param range
			Assert::IsNull( procCopy->GetParamDef(-1,IPluginICProcStepsParamDef::output) );
			Assert::IsNull( procCopy->GetParamDef(-1,IPluginICProcStepsParamDef::input));
			Assert::IsNull( procCopy->GetParamDef(3,IPluginICProcStepsParamDef::output));
			Assert::IsNull( procCopy->GetParamDef(3,IPluginICProcStepsParamDef::input));

			delete procCopy;
		}

		TEST_METHOD(BadFunctionMatchTest)
		{
		// here we are just concerned with matching a function definition to a function name and collection of actual i/o params - see ProcParamDefTest for complete bad parameter matches

			ImageProcFn *proc0 = new ImageProcFn();
																							//setup function definition
			proc0->SetFunctionName("testfn1");									
			proc0->SetFunctionDescription("a dummy function");
			proc0->AddParamDef(new ProcParamDefDouble(0, "zeroI", "zero", IPluginICProcStepsParamDef::input, 0.0, 10.0));
			proc0->AddParamDef(new ProcParamDefInt(1, "oneI", "one", IPluginICProcStepsParamDef::input, 0, 10));
			proc0->AddParamDef(new ProcParamDefString(2, "twoI", "two", IPluginICProcStepsParamDef::input, 0, 10));
			proc0->AddParamDef(new ProcParamDefDouble(0, IPluginICProcStepsParamAttribOutputNameResult, "zero", IPluginICProcStepsParamDef::output, 0.0, 10.0));

			Assert::IsTrue(proc0->Validate());												//validate function

			ProcParamsInputs inputs;														//setup actual i/o parameters - these might be read from an IEF
			ProcParamsOutputs outputs;

			Assert::IsFalse( proc0->IsMatch("testfn1", &outputs, &inputs));		//missing i/o params

			Assert::IsTrue( inputs.AddParam(new ProcParamDouble(0, "zeroI", IPluginICProcStepsParam::input, 1.0)));
			Assert::IsTrue( inputs.AddParam(new ProcParamInt(1, "oneI", IPluginICProcStepsParam::input, 2)));
			Assert::IsTrue( inputs.AddParam(new ProcParamString(2, "twoI", IPluginICProcStepsParam::input, "hello")));
			Assert::IsFalse( inputs.IsValid());
			Assert::IsTrue( inputs.Validate());
			Assert::IsTrue( inputs.IsValid());

			Assert::IsFalse( proc0->IsMatch("testfn1", &outputs, &inputs));		//missing output params

			Assert::IsTrue( outputs.AddParam(new ProcParamDouble(0, IPluginICProcStepsParamAttribOutputNameResult, IPluginICProcStepsParam::output, 1.0)));
			Assert::IsFalse( outputs.IsValid());
			Assert::IsTrue( outputs.Validate());
			Assert::IsTrue( outputs.IsValid());

			Assert::IsFalse(  proc0->IsMatch("xxxx", &outputs, &inputs));		//params ok, but bad name
			Assert::IsTrue( proc0->IsMatch("testfn1", &outputs, &inputs));		//name now OK, so match!

			Assert::IsTrue( inputs.AddParam(new ProcParamInt(3, "threeI", IPluginICProcStepsParam::input, 2)));
			Assert::IsFalse( proc0->IsMatch("testfn1", &outputs, &inputs));		//extra input param, 
		
			delete proc0;
		}

		TEST_METHOD(DynLibAddListTest)
		{
					//what happens in LoadLibs
					//		foreach (dLLfile in directory)
					//		{
					//			if ( ptr = CreateImageProcStepLibDyn(dLLfile)) //ptr is type IImageProcStepLibDyn and ptr->GetInterfaceVersion() == 0
					//			{
					//				ImageProcStepLibAuto *myLib1 = new ImageProcStepLibAuto();
					//					EITHER
					//				_LibsAuto->CopyAddLib(ptr->GetLibApi()));	
					//					OR
					//				myLib->SetFileName(ptr->GetName();
					//				myLib->SetPathFileName(directory, dLLfile);
					//				...	//add enough info to support SaveLibFunctionDefsToIef()
					//				_LibsAuto->AddLib(myLib));
					//			}
					//		}
					//
					//what happens in RunExpt
					//		foreach (xmlProcStep in ief.header)
					//		{
					//			ImageProcStepLibAuto *runLib = '\0';
					//			if ((runLib = _LibdAuto->Find(xmlProcStep->GetLibName())
					//			{
					//				if(runLib->IsMatch(xmlProcStep))
					//				{
					//					if ( ptr = CreateImageProcStepLib(runLib->GetPathFileName())) //ptr is type IImageProcStepLibDyn and ptr->GetInterfaceVersion() == 0
					//						ptr->Run(xmlProcStep->GetFnName(), xmlProcStep->GetInputParams(), xmlProcStep->GetOutputParams());
					//				}
					//			}
					//		}
					//
		}

	};
}